﻿using System;
using DynORM.Enums;
using System.Collections.Generic;
using System.Collections;

namespace DynORM.Interfaces
{
    public interface IBLLAdaptiveMapper
    {
		#region generic methods
        //helps to use PrimaryConstructor
        T Create<T>(params object[] bllObjIDs);


        //note: we can get an exception in this case: we take an object, remove some child in one of his collection and then call Delete() on this object
        //DynORM will be able to reset all relation except one, that references on the removed in the collection childer
        //in attempt of delete we'll got exception from db that we vialate fk constraint
        //this is correct behavior. if we removed some child of one of parent's collection we should store the parent to reflect this in db 
        //and only after that delete parent or do any other operations with it

        //deletes parent object and reset all the references to it in children (in other words deletes "entity" and all its "relation")
        void Delete<T>(T bllObj);
        void Delete<T>(params object[] bllObjIDs);
        //non-cascade deletion of batch of objects
        //void DeleteBatch(object[] bllObjs); //array of objects of different type can be used, so array of object is used
        //exclude many-to-many children to make it similar to DB delete cascade fk constraint's parameter
        void DeleteCascade<T>(T bllObj);
        void DeleteCascade<T>(params object[] bllObjIDs);




        //for BLLSuperType-derived class objects we can accomplish:
        //deep read; when we retrieve all the graph of related objects from db
        //read; when we retrieve the objects and the first layer of it's chilren lazy loaded (children's children are not retrieved)
        //lazy read; when we retrieve object and it's children retrieve at the moment of the first request to properties which store them

        //for POCOs we can accomplish:
        //deep read; when we retrieve all the graph of related objects from db

        //in attempt to lazy load or upload POCO we'll get an exception. object should be of class that is BLLSuperType-derived
        //note: retrieving and storing the complete graph of related object may create a huge overhead.
        //all the *Deep methods (with postfix Deep) work with complete graph. (Implemented by means of recursive call.)

        //this method reads deeply if bllObjType is POCO; for BLLSuperType-derived retrieve the objects and the first layer of it's chilren lazy loaded
        T Read<T>(params object[] bllObjIDs);
        //these 2 methods for BLLSuperType-derived retrieve the objects and the first layer of it's chilren lazy loaded; for POCO we get an exception
        IEnumerable<T> Read<T>();
        IEnumerable<T> Read<T>(IEnumerable<BLLComparison> predicate);
        //used when we need BLLSuperType-derived to load with full graph of it's related objects 
        T ReadDeep<T>(params object[] bllObjIDs);
        //when these 3 methods are used with POCO we get an exception
        IEnumerable<T> ReadLazy<T>();
        T ReadLazy<T>(params object[] bllObjIDs);
        IEnumerable<T> ReadLazy<T>(IEnumerable<BLLComparison> predicate);


        //these 2 are used to upload completely an object that was loaded lazy previously
        IEnumerable UploadLazy<T>(T bllObj); //IEnumerable of objects not T because children can be any type
        //used when we need BLLSuperType-derived to load with full graph of it's related objects 
        IDictionary<IPrimaryKey, object> UploadLazyDeep<T>(T bllObj); //Dictionary of objects not T, because children can any type
        //this auxiliary method can be used in logic like Identity map in cases when uploaded object already were in the Identity map
        //(in some complecate cases they could be modified and uploaded version doesn't contain the modificatioin so it's important to substitute 
        // newly uploaded child-object in parent with version that is in Identity map and contains the modifications)
        void SubstituteChildInObj<T1, T2>(T1 parent, T2 newchild, T2 oldchild);


        //for BLLSuperType-derived class objects we can accomplish:
        //deep write; when we store all the graph of related objects in db (don't implemented, when we call WriteDeep passing 
        //in BLLSuperType-derived class object "write loaded" logic is performed)
        //write loaded; when we store only Loaded objects. Ghost don't need to be stored because list children object couldn't be 
        //modified as they weren't even loaded (so "relation" shouldn't be updated and child's "entity" too). in this case we automaticly 
        //get the part of graph of related objects which shold be updated in db
        //write; stores the object and list of the first layer of its children (children's children are not stored) (in other words we store "entity" and its "relation")

        //for POCOs we can accomplish:
        //deep write; when we store all the graph of related objects in db
        //write; stores the object and list of the first layer of its children (children's children are not stored) (in other words we store "entity" and its "relation")

        //stores the object and list of the first layer of its children (children's children are not stored)
        void Write<T>(T bllObj, bool updateStructure);
        //stores each object in the batch and list of the first layer of its children (children's children are not stored)
        //void WriteBatch(object[] bllObjs, bool updateStructure); //batch can consist of objects of different types, so array of objects is used
        //for POCO it just store all the graph and for BLLSuperType-derived class it stores subgraph ("write loaded" logic is performed)
        void WriteDeep<T>(T bllObj, bool updateStructure);


        //can be used to separate logic that updates table structure from logic that inserts or updates records in different transactions. it can be usefull when RDBMS sopports only one DDL statment per transaction (like Oracle or MySQL do)
        void UpdateStructure<T>();

	#endregion


        #region methods
        //helps to use PrimaryConstructor
        object Create(Type bllObjType, params object[] bllObjIDs);


        //note: we can get an exception in this case: we take an object, remove some child in one of his collection and then call Delete() on this object
        //DynORM will be able to reset all relation except one, that references on the removed in the collection childer
        //in attempt of delete we'll got exception from db that we vialate fk constraint
        //this is correct behavior. if we removed some child of one of parent's collection we should store the parent to reflect this in db 
        //and only after that delete parent or do any other operations with it

        //deletes parent object and reset all the references to it in children (in other words deletes "entity" and all its "relation")
        void Delete(object bllObj);
        void Delete(Type bllObjType, params object[] bllObjIDs);
        //non-cascade deletion of batch of objects
        void DeleteBatch(object[] bllObjs);
        //exclude many-to-many children to make it similar to DB delete cascade fk constraint's parameter
        void DeleteCascade(object bllObj);
        void DeleteCascade(Type bllObjType, params object[] bllObjIDs);




        //for BLLSuperType-derived class objects we can accomplish:
        //deep read; when we retrieve all the graph of related objects from db
        //read; when we retrieve the objects and the first layer of it's chilren lazy loaded (children's children are not retrieved)
        //lazy read; when we retrieve object and it's children retrieve at the moment of the first request to properties which store them

        //for POCOs we can accomplish:
        //deep read; when we retrieve all the graph of related objects from db

        //in attempt to lazy load or upload POCO we'll get an exception. object should be of class that is BLLSuperType-derived
        //note: retrieving and storing the complete graph of related object may create a huge overhead.
        //all the *Deep methods (with postfix Deep) work with complete graph. (Implemented by means of recursive call.)

        //this method reads deeply if bllObjType is POCO; for BLLSuperType-derived retrieve the objects and the first layer of it's chilren lazy loaded
        object Read(Type bllObjType, params object[] bllObjIDs);
        //these 2 methods for BLLSuperType-derived retrieve the objects and the first layer of it's chilren lazy loaded; for POCO we get an exception
        IEnumerable<object> Read(Type bllObjType);
        IEnumerable<object> Read(Type bllObjType, IEnumerable<BLLComparison> predicate);
        //used when we need BLLSuperType-derived to load with full graph of it's related objects 
        object ReadDeep(Type bllObjType, params object[] bllObjIDs);
        //when these 3 methods are used with POCO we get an exception
        IEnumerable<object> ReadLazy(Type bllObjType);
        object ReadLazy(Type bllObjType, params object[] bllObjIDs);
        IEnumerable<object> ReadLazy(Type bllObjType, IEnumerable<BLLComparison> predicate);


        //these 2 are used to upload completely an object that was loaded lazy previously
        IEnumerable UploadLazy(object bllObj);
        //used when we need BLLSuperType-derived to load with full graph of it's related objects 
        IDictionary<IPrimaryKey, object> UploadLazyDeep(object bllObj);
        //this auxiliary method can be used in logic like Identity map in cases when uploaded object already were in the Identity map
        //(in some complecate cases they could be modified and uploaded version doesn't contain the modificatioin so it's important to substitute 
        // newly uploaded child-object in parent with version that is in Identity map and contains the modifications)
        void SubstituteChildInObj(object parent, object newchild, object oldchild);


        //for BLLSuperType-derived class objects we can accomplish:
        //deep write; when we store all the graph of related objects in db (don't implemented, when we call WriteDeep passing 
        //in BLLSuperType-derived class object "write loaded" logic is performed)
        //write loaded; when we store only Loaded objects. Ghost don't need to be stored because list children object couldn't be 
        //modified as they weren't even loaded (so "relation" shouldn't be updated and child's "entity" too). in this case we automaticly 
        //get the part of graph of related objects which shold be updated in db
        //write; stores the object and list of the first layer of its children (children's children are not stored) (in other words we store "entity" and its "relation")

        //for POCOs we can accomplish:
        //deep write; when we store all the graph of related objects in db
        //write; stores the object and list of the first layer of its children (children's children are not stored) (in other words we store "entity" and its "relation")

        //stores the object and list of the first layer of its children (children's children are not stored)
        void Write(object bllObj, bool updateStructure);
        //stores each object in the batch and list of the first layer of its children (children's children are not stored)
        void WriteBatch(object[] bllObjs, bool updateStructure);
        //for POCO it just store all the graph and for BLLSuperType-derived class it stores subgraph ("write loaded" logic is performed)
        void WriteDeep(object bllObj, bool updateStructure);

        //can be used to separate logic that updates table structure from logic that inserts or updates records in different transactions. it can be usefull when RDBMS sopports only one DDL statment per transaction (like Oracle or MySQL do)
        void UpdateStructure(Type bllObjType);

        #endregion
    }
}
